#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "log.h"
#include "dispatch.h"

static int32_t dispatch_init(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    
    INIT_LIST_HEAD(&(pdispatch->head));
    lock_init(&(pdispatch->lock));
    lock_init(&(pdispatch->notify_lock));
    wait_init(&(pdispatch->wait));
    
    ret = pdispatch->op->create_uconfig(pdispatch);
    if(ret < 0){
        DBG(DBG_ERR, "create uconfig error\n");
        return ret;
    }

    return ret;
}

static int32_t dispatch_release(struct dispatch* pdispatch)
{
	pdispatch->op->destroy_uconfig(pdispatch);

    lock_destroy((&pdispatch->notify_lock));
    lock_destroy((&pdispatch->lock));
    wait_destroy((&pdispatch->wait));
    return 0;
}

static int32_t dispatch_start(struct dispatch* pdispatch)
{
    int32_t ret = 0;  
    
    

	return ret;
}

static int32_t dispatch_stop(struct dispatch* pdispatch)
{
    int32_t ret = 0;

	return ret;
}

static int32_t dispatch_create_uconfig(struct dispatch* pdispatch)
{
	int32_t ret;

	ret = create_init_uconfig(&pdispatch->puconfig);
	if(ret < 0){
		DBG(DBG_ERR, "create uconfig error\n");
		return ret;
	}

    return 0;
}

static int32_t dispatch_destroy_uconfig(struct dispatch* pdispatch)
{
    if(pdispatch->puconfig){
        release_destroy_uconfig(pdispatch->puconfig);
        pdispatch->puconfig = NULL;
	}

	return 0;
}

static int32_t dispatch_register_notify(struct dispatch* pdispatch, int32_t event, dispatch_event_notify notify, void* object)
{
    struct dispatch_event_action* paction;
    if(!notify || (event <= DISPATCH_EVENT_NONE) || (event >= DISPATCH_EVENT_MAX)){
        return -EINVAL;
    }
    paction = (struct dispatch_event_action*)malloc(sizeof(struct dispatch_event_action));
    if(!paction){
        DBG(DBG_ERR,"malloc error\n");
        return -ENOMEM;
    }
    paction->notify = notify;
    paction->object = object;
    lock(&(pdispatch->notify_lock));
    paction->next = pdispatch->paction[event];
    pdispatch->paction[event] = paction;
    unlock(&(pdispatch->notify_lock));
    return 0;
}

static int32_t dispatch_unregister_notify(struct dispatch* pdispatch, int32_t event, void* object)
{
    struct dispatch_event_action *paction,* ptmp;
    if((event <= DISPATCH_EVENT_NONE) || (event >= DISPATCH_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(pdispatch->notify_lock));
    paction = pdispatch->paction[event];
    if(paction){
        if(paction->object == object){
            pdispatch->paction[event] = paction->next;
            free(paction);
        }else{
            while(paction->next){
                if(paction->next->object == object){
                    ptmp = paction->next;
                    paction->next = ptmp->next;
                    free(ptmp);
                    break;
                }
                paction = paction->next;
            }
        }
    }
    unlock(&(pdispatch->notify_lock));
    return 0;
}

static int32_t dispatch_trigger_notify(struct dispatch* pdispatch, int32_t event, void* context)
{
    struct dispatch_event_action* paction;
    if((event <= DISPATCH_EVENT_NONE) || (event >= DISPATCH_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(pdispatch->notify_lock));
    paction = pdispatch->paction[event];
    while(paction){
        paction->notify(pdispatch, event, paction->object, context);
        paction = paction->next;
    }
    unlock(&(pdispatch->notify_lock));
    return 0;
}

static struct dispatch_operation dispatch_op =
{
    .init = dispatch_init,
    .release = dispatch_release,

	.start = dispatch_start,
	.stop = dispatch_stop,
	
	.create_uconfig = dispatch_create_uconfig,
	.destroy_uconfig = dispatch_destroy_uconfig,
    
    .register_notify = dispatch_register_notify,
    .unregister_notify = dispatch_unregister_notify,
    .trigger_notify = dispatch_trigger_notify,
};

int32_t create_init_dispatch(struct dispatch** pdispatch)
{
    int32_t ret;
    struct dispatch* ptmp;
    (*pdispatch) = (struct dispatch*)malloc(sizeof(struct dispatch));
    if(!(*pdispatch)){
        DBG(DBG_ERR,"malloc error\n");
        return -ENOMEM;
    }
    ptmp = *pdispatch;
    memset(ptmp, 0, sizeof(struct dispatch));
    ptmp->op = &dispatch_op;
    ret = ptmp->op->init(ptmp);
    if(ret < 0){
        DBG(DBG_ERR,"init error\n");
        release_destroy_dispatch(ptmp);
        return ret;
    }
    return 0;
}

void release_destroy_dispatch(struct dispatch* pdispatch)
{
    if(pdispatch){
        pdispatch->op->release(pdispatch);
        free(pdispatch);
    }
}

